How We Expect Programs To Behave

A recent assignment for a university course I'm taking has me thinking about the way we might expect the behavior of a program to occur. Living in a UNIX context (due to using Linux as my daily driver), I generally expect the utility programs I use to go through the following process:

  1. Start up.
  2. Gather input.
  3. Process input.
  4. Output result or show an error, and quit.

This sounds pretty normal in my eyes, but some teachers are inclined more towards the process of:

  1. Start program.
  2. Program asks for input.
  3. If input is correct, process it; if not, loop back and ask user to try again.

This process is expected to happen for every single input expected from the user for the lifetime of the process. This comes off as pretty clunky, and the code reflects that.

use std::io;
use regex::Regex;

fn main() {
    let re_year = Regex::new(r"^[1-9]+$").unwrap();
    let re_confirm = Regex::new(r"^(?:y|n)$").unwrap();

    loop {
        let mut year_input = String::new();
        loop {
            println!("Ingrese un año: ");
            io::stdin().read_line(&mut year_input).expect("Fallo");

            if re_year.is_match(year_input.trim()) {
                break;
            }
            println!("La entrada necesita ser de números solamente");
        }

        let clean_input = year_input.trim();
        if let Ok(year) = clean_input.parse::<u32>() {
            if is_leap_year(year) {
                println!("El año {} es bisiesto", clean_input);
            } else {
                println!("El año {} NO es bisiesto", clean_input);
            }
        } else {
            println!("La entrada no es un número válido");
        }

        let mut must_confirm = true;
        while must_confirm {
            println!("Desea continuar? [y/n]");
            let mut confirm = String::new();
            io::stdin().read_line(&mut confirm).expect("confirmación falló");
            if re_confirm.is_match(confirm.trim()) {
                must_confirm = false;
                if confirm.trim() == "n" {
                    return;
                }
            }
        }
    }
}

fn is_leap_year(year: u32) -> bool {
    year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
}

As you can see, there are quite a few loops that exist only to nag the user into providing a correct input. This, in my eyes, becomes an incredibly annoying way of using a program.

Input validation in this style might work well when it comes to using graphically presented forms, where the person can re-evaluate the information they introduced; it is not so for interactive inputs in a one-shot CLI program since it's incredibly uncomfortable to "go back" and edit different inputs you've previously provided.

A much simpler (and actually closer in practicality to normal web forms) approach is to use flags or command line arguments, this allows the person to edit, in a single view, all the information they are passing to the program even before said program executes. This also allows for a much more standard way of designing said program, as most languages either provide a library designed for CLI applications, or the ecosystem has a few third-party libraries for the same purpose. In the case of Rust (the language used to make the program above), there's the (clap crate)[https://docs.rs/clap/latest/clap/], which provides many useful functions for this endeavor.

Dusting Off the Blog

I've certainly neglected posting here in a while, I'm gonna fix that. I have quite a few post ideas I'm working on, hopefully a handful of them will become full-on posts.

I have managed to set up automatic deployment using Sourcehut Pages, which is very neat; I'm definitely gonna make a post about that soon.

Outside Your Comfort Zone

Intro

Recently, I had a class which required conversion between a few numeric systems such as binary (base2), decimal (the "normal" one we use, base10), and base16.

(It was actually a couple more, but for this blogpost only those three are relevant.)

A classmate of mine asked the professor if it was alright for him, in a conversion between binary and base16, to convert to decimal as a middle step. This initially seems as a good practice. Since decimal is a system that we've all used since we learned to count, it makes sense to want to use it as a middleground between two unknown systems.

In a situation where time and success are critical, it's natural to want to stick to what you already know.

The Problem with this Approach

In principle, I don't necessarily advise people to stray from their comfort zone when performing a critical task; when dealing with little margin for error, one should always exercise caution and stick to what works. However, in class, it's experimentation time.

Reasoning

For numerical systems that share a common base (base16 is, technically, base2^4), conversion is actually simpler than it would be using a different system with an incongruous base as a middleground. To prove this, let's do a little math...

The Smart, But "Uncomfortable" Way

For a base16 number, each of its figures can be represented as 4 figures in binary. Therefore, if the number were to be 52 in base16, then the 5 would be 0101 and the 2 would be 0010. Problem solved. Steps taken: 2. (3, if you wanna count writing 01010010 together as a step.)

The "Comfortable", But Convoluted Way

Whereas converting it to decimal would entail multiplying 5 by 16 and then adding 2 to that, and then dividing 84 (the result of the previous conversion) by 2 over and over again until the remainder is either 1 or 0, that's six divisions you have to carry out in order to convert a decimal number of two figures. Which then nets you 0101 0010, which is the exact same result we reached earlier. Steps: 7 (if you consider conversion from base16 to decimal a single step, and I know you don't.)

Conclusion

This was merely an example of the myriad of situations in which stretching your limits a bit can save you tons of time. If you've read my previous post, then you know that, even though learning to use it may seem difficult at first, it'll definitely save you lots of headaches later on.

You Should Use Git

The Problem

I've noticed that, at the university I go to, many of my peers often have trouble learning to use git (or any form of VCS, really) and, therefore, opt to simply not use it at all. I believe this is a mistake, by not using any sort of version control you subject yourself to a whole host of difficulties and challenges if you simply let your lack of comfort with such a tool get to you.

Managing File Versions

This one seems like the obvious argument to make for a tool with "version control" in its name, but I've noticed that people who don't use a VCS often try to make up for this lack of control by having multiple copies of the same file. This isn't particularly wrong per sé, and it initially seems to be a clever workaround. However, most projects at my university often last between 3 an 15 weeks. Would you honestly trust yourself to handle multiple versions of various projects effectively? Every single one?

This is where a VCS comes in, long gone are the days of needing a folder containing (I'm using Java as an example language because that's what my university uses as an introductory language)

project_directory
|-projectv1.java
|-projectv2.java
|-projectv3.java

littered all over your project directory. Git handles this versioning by storing the changes you've progressively made to your files via "commits". These are atomic "bundles" of changes you decide to make permanent on your project. This way, Git slowly builds up a history of all the changes you've made ever since the first commit.

Rolling back

The main reason I've seen stated for multiple versions of a file is the desire for being able to roll back any changes you've made to a file. Git also handles this very well, you simply have to look at your project history, pick a commit you want to roll back to, issue the command and boom, you've gone back in time!

Backups / Multiple Copies

Another of the main draws of a VCS is the ability to store remote backups in case of a catastrophic event. Several times some of my colleagues have told stories about how a disgruntled former teammate decided to wipe out a project repository or about how they unintentionally deleted the project repo from their own machine.

Luckily, a VCS also has that situation in mind. By allowing for remote repositories, a VCS makes it easy to store remotely all of your project history and current state. All you need to do is use a remote server such as Codeberg or Sourcehut. These will take care of storing your remote repo safely.

Having this system in place, you can back up your commits by simply running git push, and git will handle the job of moving the changes you've made in your local repo over to your remote repo. If you want to download the changes you've (or someone else has) made, you simply run git pull.

And so, if something happens to my local repo, the remote one will still be intact. The only commits lost would be the ones I hadn't pushed at the time of loss. Conversely, if anything catastrophic happened to my remote repo (or repos, Git allows for multiple remotes and such), then the only commits that I'd lose would be those that I hadn't pulled from there yet.

NOTE: Remember that if you don't regularly push your changes to your remote repo then there's nothing Git and other VCSs can do to save you.

Conclusion

Git is capable of many more things than what I mentioned here, but the benefits I listed are what would've saved many projects ~90% of the time. Being honest, you're simply depriving yourself from proper project management if you opt to not use a VCS. It might not be the easiest tool to learn, but it'll be the one tool you want most whenever something goes wrong with your project. If you're a student in anything concerning handling plaintext files such as code, seriously, you should use Git!

Intro

This is is the very first post in my new blog, this time I'll be using Hugo to generate it instead of WordPress.

In general, the experience has been very straightforward, I write my entries in markdown and Hugo handles the conversion to HTML pages.

I don't hate WordPress at all, and consider it to still be a valuable service and overall website builder, but I tend to lean towards the more manual experiences when it comes to computers.